package com.gyy.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;

/**
 * @Author gmd
 * @Description IEEE754标准协议数值转换工具类 (-1)^S * 1.M * 2^(E-127)
 * https://baike.baidu.com/item/IEEE%20754/3869922
 *
 * @Date 2022-01-20 09:06:13
 */
@Slf4j
public class IEEE754Utils {

    /**
     * 单精度浮点数转十六进制
     *
     * @param data 单精度浮点数
     * @return 十六进制数
     */
    public static String floatToHex(float data) {
        // %X表示转十六进制大写，%x表示转十六进制小写
        return String.format("%X", Float.floatToIntBits(data));
    }


    /**
     * 双精度浮点数转十六进制
     *
     * @param data 双精度浮点数
     * @return 十六进制数
     */
    public static String doubleToHex(double data) {
        // %X表示转十六进制大写，%x表示转十六进制小写
        return String.format("%X", Double.doubleToLongBits(data));
    }


    /**
     * 32位二进制转单精度浮点数
     *
     * @param binary
     * @return
     */
    public static float binaryToFloat(String binary) {
        if (binary.length() != 32) {
            System.out.println("IEEE754标准中，二进制数转单精度浮点数必须为32位");
            return 0.0f;
        } else {
            int sign = binary.charAt(0) == '0' ? 1 : -1;
            String baseBinary = "1" + binary.substring(9);
            float baseFloat = 0.0f;
            int exp = Integer.valueOf(binary.substring(1, 9), 2) - 127;
            for (int i = 0; i < 24; i++) {
                baseFloat += (baseBinary.charAt(i) == '1' ? Math.pow(2, -i) : 0);
            }
            //单精度浮点数最多可以保证7位十进制有效数字
            return Float.valueOf(String.format("%.7f", sign * baseFloat * Math.pow(2, exp)));
        }
    }


    /**
     * 64位二进制转双精度浮点数
     *
     * @param binary
     * @return
     */
    public static double binaryToDouble(String binary) {
        if (binary.length() != 64) {
            System.out.println("IEEE754标准中，二进制数转双精度浮点数必须为64位");
            return 0.0d;
        } else {
            int sign = binary.charAt(0) == '0' ? 1 : -1;
            String baseBinary = "1" + binary.substring(12);
            double baseDouble = 0.0d;
            int exp = Integer.valueOf(binary.substring(1, 12), 2) - 1023;
            for (int i = 0; i < 53; i++) {
                baseDouble += (baseBinary.charAt(i) == '1' ? Math.pow(2, -i) : 0);
            }
            // 双精度浮点数最多可以保证15位十进制有效数字
            return Double.valueOf(String.format("%.15f", sign * baseDouble * Math.pow(2, exp)));
        }
    }


    /**
     * 十六进制转浮点数
     *
     * @param data 十六进制数
     * @return 浮点数
     */
    public static float hexToFloat(String data) {
        return Float.intBitsToFloat(Integer.parseInt(data, 16));
    }


    /**
     * 十六进制数组转二进制数组
     *
     * @param data 十六进制数组
     * @return 二进制数组
     */
    public static byte[] hexToBits(String[] data) {
        byte[] result = new byte[data.length];
        for (int i = 0; i < data.length; i++) {
            result[i] = Byte.parseByte(data[i], 16);
        }
        return result;
    }


    /**
     * 二进制数组转十六进制数组
     *
     * @param data 二进制数组
     * @return 十六进制数组
     */
    public static String bitsToHexStr(byte[] data) {
        String[] result = new String[data.length];

        for (int i = 0; i < data.length; i++) {
            result[i] = String.format("%02X", data[i]);
        }
        return StringUtils.join(result, " ");
    }


    /**
     * 测试main方法
     */
    public static void main(String[] args) {
        String hex = floatToHex(123.456f);
        log.info(hex);

        String hex2 = doubleToHex(123.456d);
        log.info(hex2);

        float fValue = hexToFloat("42F6E979");
        log.info(String.valueOf(fValue));

        byte[] bytes = hexToBits("01 0C 10 2F".split(" "));
        log.info(Arrays.toString(bytes));

        String hexStr = bitsToHexStr(new byte[]{1, 12, 16, 47});
        log.info(hexStr);
    }

}
